

#pragma once

#include "core.h"

namespace Blox
{
	class Vec
	{
	public:

		Vec ( ) : x(0.0f), y(0.0f), z(0.0f) { }
		Vec ( float _x, float _y, float _z ) : x(_x), y(_y), z(_z) { }
		//Vec ( Vec const & _v ) : x(_v.x), y(_v.y), z(_v.z) { }

		static Vec const Right;
		static Vec const Up;
		static Vec const Forward;
		static Vec const AllZero;
		static Vec const AllOne;

		// Static Functions
		static inline void Add ( Vec & r, Vec const & a, Vec const & b ) { r.x=a.x+b.x; r.y=a.y+b.y; r.z=a.z+b.z; }
		static inline void Sub ( Vec & r, Vec const & a, Vec const & b ) { r.x=a.x-b.x; r.y=a.y-b.y; r.z=a.z-b.z; }
		static inline void Mul ( Vec & r, Vec const & a, Vec const & b ) { r.x=a.x*b.x; r.y=a.y*b.y; r.z=a.z*b.z; }
		static inline void Scale ( Vec & r, Vec const & a, float s ) { r.x=a.x*s; r.y=a.y*s; r.z=a.z*s; }
		static inline void Cross ( Vec & r, Vec const & a, Vec const & b ) { }
		static inline float Dot ( Vec const & a, Vec const & b ) { return (a.x*b.x)+(a.y*b.y)+(a.z*b.z); }
		static inline float MagSqr ( Vec const & a ) { return Dot(a,a); }
		static inline float Mag ( Vec const & a ) { return FSqrt(MagSqr(a)); }
		static inline void Min ( Vec & r, Vec const & a, Vec const & b ) { r.x=FMin(a.x,b.x); r.y=FMin(a.y,b.y); r.z=FMin(a.z,b.z); }
		static inline void Max ( Vec & r, Vec const & a, Vec const & b ) { r.x=FMax(a.x,b.x); r.y=FMax(a.y,b.y); r.z=FMax(a.z,b.z); }
		static inline void Lerp ( Vec & r, Vec const & a, Vec const & b, float t ) { r.x=FLerp(a.x,b.x,t); r.y=FLerp(a.y,b.y,t); r.z=FLerp(a.z,b.z,t); }
		static inline float Norm ( Vec & r, Vec const & a ) { float d = Mag(a); Scale(r,a,1.0f/d); return d; }
		static inline bool AllGreaterThan ( Vec const & a, Vec const & b ) { return a.x>b.x && a.y>b.y && a.z>b.z; }
		static inline bool AllLessThan ( Vec const & a, Vec const & b ) { return a.x<b.x && a.y<b.y && a.z<b.z; }

		// Internal Functions
		inline Vec const & Add ( Vec const & a ) { Add( *this, *this, a ); return *this; }
		inline Vec const & Sub ( Vec const & a ) { Sub( *this, *this, a ); return *this; }
		inline Vec const & Mul ( Vec const & a ) { Mul( *this, *this, a ); return *this; }
		inline Vec const & Scale ( float s ) { Scale( *this, *this, s ); return *this; }
		inline Vec const & Cross ( Vec const & a, Vec const & b ) { Cross( *this, a, b ); return *this; }
		inline float Dot ( Vec const & a ) { return Dot( *this, a ); }
		inline float MagSqr ( ) { return Dot( *this, *this ); }
		inline float Mag ( ) { return FSqrt(MagSqr(*this)); }
		inline Vec const & Min ( Vec const & a ) { Min( *this, *this, a ); return *this; }
		inline Vec const & Max ( Vec const & a ) { Max( *this, *this, a ); return *this; }
		inline float Norm ( Vec const & a ) { return Norm( *this, a ); }

		// Basic [Add,Sub,Scale]
		inline Vec operator+ ( Vec const & a ) const { Vec r; Add(r,*this,a); return r; }
		inline Vec operator- ( Vec const & a ) const { Vec r; Sub(r,*this,a); return r; }
		inline Vec operator* ( float const s ) const { Vec r; Scale(r,*this,s); return r; }
		inline bool operator< ( Vec const & a ) const { return AllLessThan(*this,a); }
		inline bool operator> ( Vec const & a ) const { return AllGreaterThan(*this,a); }
		inline Vec& operator+= ( Vec const & a ) { Add(*this,*this,a); return *this; }
		inline Vec& operator-= ( Vec const & a ) { Sub(*this,*this,a); return *this; }
		inline Vec& operator*= ( float const s ) { Scale(*this,*this,s); return *this; }
	
		// Special [Dot,Cross]
		inline float operator* ( Vec const & a ) const { return Dot(*this,a); }
		inline Vec operator^ ( Vec const & a ) const { Vec r; Cross(r,*this,a); return r; }

		// Self Functions [Norm|Zero|One]
		inline float Norm ( ) { return Norm(*this,*this); }
		inline void Zero ( ) { x=y=z=0.0f; }
		inline void One ( ) { x=y=z=1.0f; }



		float x;
		float y;
		float z;
	};

	class Vec4
	{
	public:
		Vec4 ( ) : w(0.0f) { }
		Vec4 ( float _x, float _y, float _z, float _w = 1.0f ) : v(_x,_y,_z), w(_w) { }
		Vec4 ( Vec const & _v, float _w = 1.0f ) : v(_v), w(_w) { }

		Vec v;
		float w;
	};


	class AABB;

	class Bounds
	{
	public:
		Bounds ( ) { }
		Bounds ( Vec const & rMin, Vec const & rMax ) : vMin(rMin), vMax(rMax) { }
		Bounds ( AABB const & rBounds );

		void Set ( AABB const & rBox );

		Vec vMin;
		Vec vMax;
	};

	class AABB
	{
	public:
		AABB ( ) { }
		AABB ( Vec const & rCentre, Vec const & rExtents ) : vCentre(rCentre), vExtents(rExtents) { }
		AABB ( Bounds const & rBounds );

		void Set ( Bounds const & rBounds );

		void GetCornerExtents ( Vec * pCorners, float fScale = 1.0f ) const;

		inline float LongestSide ( ) const { return FMax( FMax( vExtents.x*2.0f, vExtents.y*2.0f ), vExtents.z*2.0f ); }

		inline bool Contains ( Bounds const & rOther ) const { return (rOther.vMax<(vCentre+vExtents)) && (rOther.vMin>(vCentre-vExtents)); }

		Vec vCentre;
		Vec vExtents;
	};

	struct InputVert
	{
		InputVert( Vec const & _p, u32 const & _c ) : p(_p), c(_c) {}
		Vec p;
		u32 c;
	};

}
